

#include "materialmanager.h"
#include <QFile>
#include <QDomDocument>
#include "session.h"
#include <gfx/iprogram.h>
#include <gfx/material.h>

#define FLAG(v, f)  (((v) & (f)) == (f))

#define WRAP_REPEAT         "Repeat"
#define WRAP_CLAMP          "Clamp"
#define WRAP_CLAMPTOEDGE    "ClampToEdge"
#define WRAP_CLAMPTOBORDER  "ClampToBorder"

#define FILTER_ANISOTROPIC  "Anisotropic"
#define FILTER_TRILINEAR    "Trilinear"
#define FILTER_BILINEAR     "Bilinear"
#define FILTER_NEAREST      "Nearest"

#define MIPMAP_ON						"true"
#define MIPMAP_OFF					"false"

#define PASS_DIFFUSE        "Diffuse"
#define PASS_DEPTH          "Depth"
#define PASS_SHADOW         "Shadow"
#define PASS_STENCIL        "Stencil"

#define SHADER_VERTEX       "Vertex"
#define SHADER_FRAGMENT     "Fragment"


#define ELEMENT_TEXTURES    "textures"
#define ELEMENT_TEXTURE     "texture"
#define ELEMENT_TEXTUREPACK "texturepack"
#define ELEMENT_FILENAME    "filename"
#define ELEMENT_WRAP        "wrap"
#define ELEMENT_FILTER      "filter"
#define ELEMENT_MIPMAP			"mipmap"
#define ELEMENT_ANISOTROPY  "anisotropy"

#define ELEMENT_SHADERS     "shaders"
#define ELEMENT_SHADER      "shader"
#define ELEMENT_TYPE        "type"
#define ELEMENT_CODE        "code"

#define ELEMENT_PROGRAMS    "programs"
#define ELEMENT_PROGRAM     "program"
#define ELEMENT_EFFECTPROGRAM     "effectprogram"
#define ELEMENT_SHADERPROGRAM	"shaderprogram"
#define ELEMENT_REQUIREMENTS "requirements"
#define ELEMENT_REQUIREMENT "requirement"
#define ELEMENT_VERSION     "version"
#define ELEMENT_BINDINGS    "bindings"
#define ELEMENT_BINDING     "binding"

#define ELEMENT_EFFECTS     "effects"
#define ELEMENT_EFFECT      "effect"
#define ELEMENT_STAGE       "stage"
#define ELEMENT_SOLVER			"solver"

#define ELEMENT_MATERIALS   "materials"
#define ELEMENT_MATERIAL    "material"
#define ELEMENT_DEFAULT     "default"
#define ELEMENT_SPEC        "spec"
#define ELEMENT_AMBIENT     "ambient"
#define ELEMENT_DIFFUSE     "diffuse"
#define ELEMENT_SPECULAR    "specular"
#define ELEMENT_SHININESS   "shininess"
#define ELEMENT_EMISSION    "emission"
#define ELEMENT_WRITEDEPTH  "writedepth"
#define ELEMENT_TESTDEPTH   "testdepth"
#define ELEMENT_TRANS       "transparency"

#define ATTRIB_NAME         "name"
#define ATTRIB_CATEGORY     "category"
#define ATTRIB_ITEM_NAME		"itemname"
#define ATTRIB_BINDING      "binding"
#define ATTRIB_PASS         "pass"
#define ATTRIB_STAGE        "stage"
#define ATTRIB_TYPE					"type"


namespace
{
void read_color (const QDomElement& element, ceColor4f& col)
{
  if (element.isNull()) return;
  col.r = element.attribute("r").toFloat();
  col.g = element.attribute("g").toFloat();
  col.b = element.attribute("b").toFloat();
  col.a = element.attribute("a").toFloat();
}

void write_color (QDomElement& element, const ceColor4f& col)
{
  element.setAttribute("r", col.r);
  element.setAttribute("g", col.g);
  element.setAttribute("b", col.b);
  element.setAttribute("a", col.a);
}
}

/* ******************************************************
 * ******************************************************
 *  Loading
 * ******************************************************
 * ******************************************************/

bool MaterialManager::Load(int dv)
{
  if (FLAG(dv, DV_Textures) && !LoadTextures())
    {
      return false;
    }
  if (FLAG(dv, DV_Shaders) && !LoadShaders())
    {
      return false;
    }
  if (FLAG(dv, DV_Programs) && !LoadPrograms())
    {
      return false;
    }
  if (FLAG(dv, DV_Effects) && !LoadEffects())
    {
      return false;
    }
  if (FLAG(dv, DV_Materials) && !LoadMaterials())
    {
      return false;
    }
  return true;
}

bool MaterialManager::LoadTextures()
{
  QFile file (Session::Get()->GetProjectFolder() + "/textures.xtxts");
  if (!file.open(QIODevice::ReadOnly))
    {
      return false;
    }

  QDomDocument doc;
  if (!doc.setContent(&file))
    {
      file.close();
      return false;
    }
  file.close();

  QDomElement texturesElement = doc.firstChildElement(ELEMENT_TEXTURES);
  if (texturesElement.isNull())
    {
      return false;
    }
  for (QDomElement textureElement = texturesElement.firstChildElement(ELEMENT_TEXTURE);
       !textureElement.isNull();
       textureElement = textureElement.nextSiblingElement(ELEMENT_TEXTURE))
    {
      QDomElement filenameElement = textureElement.firstChildElement(ELEMENT_FILENAME);
      if (filenameElement.isNull())
        {
          return false;
        }

      Texture* texture = CreateTexture();

      if (textureElement.hasAttribute(ATTRIB_CATEGORY))
        {
          texture->GetCategory().SetFullName(textureElement.attribute(ATTRIB_CATEGORY));
        }
      if (textureElement.hasAttribute(ATTRIB_ITEM_NAME))
        {
          texture->SetExportName(textureElement.attribute(ATTRIB_ITEM_NAME));
        }

      texture->SetFilename(filenameElement.text());

      QDomElement wrapElement = textureElement.firstChildElement(ELEMENT_WRAP);
      texture->SetWrap(Texture::Wrap_Default);
      if (!wrapElement.isNull())
        {
          QString wrap = wrapElement.text();
          if (wrap.compare(WRAP_REPEAT, Qt::CaseInsensitive) == 0)
            {
              texture->SetWrap(Texture::Wrap_Repeat);
            }
          else if (wrap.compare(WRAP_CLAMP, Qt::CaseInsensitive) == 0)
            {
              texture->SetWrap(Texture::Wrap_Clamp);
            }
          else if (wrap.compare(WRAP_CLAMPTOEDGE, Qt::CaseInsensitive) == 0)
            {
              texture->SetWrap(Texture::Wrap_ClampToEdge);
            }
          else if (wrap.compare(WRAP_CLAMPTOBORDER, Qt::CaseInsensitive) == 0)
            {
              texture->SetWrap(Texture::Wrap_ClampToBorder);
            }
        }

      QDomElement filterElement = textureElement.firstChildElement(ELEMENT_FILTER);
      texture->SetFilter(Texture::Filter_Default);
      if (!filterElement.isNull())
        {
          QString filter = filterElement.text();
          if (filter.compare (FILTER_ANISOTROPIC) == 0)
            {
              texture->SetFilter(Texture::Filter_Anisotropic);
            }
          else if (filter.compare(FILTER_TRILINEAR) == 0)
            {
              texture->SetFilter(Texture::Filter_Trilinear);
            }
          else if (filter.compare(FILTER_BILINEAR) == 0)
            {
              texture->SetFilter(Texture::Filter_Bilinear);
            }
          else if (filter.compare(FILTER_NEAREST) == 0)
            {
              texture->SetFilter(Texture::Filter_Nearest);
            }
        }

      QDomElement anisotropyElement = textureElement.firstChildElement(ELEMENT_ANISOTROPY);
      texture->SetAnisotropy(0);
			if (!anisotropyElement.isNull ())
				{
					bool ok;
					int value = anisotropyElement.text().toInt(&ok);
					if (ok)
						{
							texture->SetAnisotropy(value);
						}
				}

			QDomElement mipmapElement = textureElement.firstChildElement(ELEMENT_MIPMAP);
			texture->SetMipMap (Texture::MipMap_Default);
			if (!mipmapElement.isNull ())
				{
					if (mipmapElement.text () == QString ("true"))
						{
							texture->SetMipMap (Texture::MipMap_On);
						}
					else
						{
							texture->SetMipMap (Texture::MipMap_Off);
						}
				}
      texture->UpdateData ();
      AddTexture(texture);
    }
  file.close();
  return true;
}

bool MaterialManager::LoadShaders()
{
  QFile file (Session::Get()->GetProjectFolder() + "/shaders.xshds");
  if (!file.open(QIODevice::ReadOnly))
    {
      return false;
    }

  QDomDocument doc;
  if (!doc.setContent(&file))
    {
      file.close();
      return false;
    }
  file.close();

  QDomElement shadersElement = doc.firstChildElement(ELEMENT_SHADERS);
  if (shadersElement.isNull())
    {
      return false;
    }
  for (QDomElement shaderElement = shadersElement.firstChildElement(ELEMENT_SHADER);
       !shaderElement.isNull();
       shaderElement = shaderElement.nextSiblingElement(ELEMENT_SHADER))
    {
      QDomElement typeElement = shaderElement.firstChildElement(ELEMENT_TYPE);
      if (typeElement.isNull())
        {
          return false;
        }
      QDomElement codeElement = shaderElement.firstChildElement(ELEMENT_CODE);
      if (codeElement.isNull())
        {
          return false;
        }

      Shader* shader = CreateShader();

      if (shaderElement.hasAttribute(ATTRIB_CATEGORY))
        {
          shader->GetCategory().SetFullName(shaderElement.attribute(ATTRIB_CATEGORY));
        }
      if (shaderElement.hasAttribute(ATTRIB_ITEM_NAME))
        {
          shader->SetExportName(shaderElement.attribute(ATTRIB_ITEM_NAME));
        }

      QString type = typeElement.text();
      if (type.compare(SHADER_VERTEX, Qt::CaseInsensitive) == 0)
        {
          shader->SetShaderType(ST_Vertex);
        }
      else if (type.compare(SHADER_FRAGMENT, Qt::CaseInsensitive) == 0)
        {
          shader->SetShaderType(ST_Fragment);
        }


      shader->SetCode(codeElement.text());
      if (!shader->GetShader()->Compile())
        {
          printf ("CompileLog: %s\n",
                  shader->GetShader()->GetCompileInfoLog().c_str());
          fflush(stdout);
        }

      AddShader(shader);
    }

  return true;
}

bool MaterialManager::LoadPrograms()
{

  QFile file (Session::Get()->GetProjectFolder() + "/programs.xprgs");
  if (!file.open(QIODevice::ReadOnly))
    {
      return false;
    }

  QDomDocument doc;
  if (!doc.setContent(&file))
    {
      file.close();
      return false;
    }
  file.close();

  QDomElement programsElement = doc.firstChildElement(ELEMENT_PROGRAMS);

  for (QDomElement programElement = programsElement.firstChildElement(ELEMENT_PROGRAM);
       !programElement.isNull();
       programElement = programElement.nextSiblingElement(ELEMENT_PROGRAM))
    {
      Program* prog = CreateProgram();

      if (programElement.hasAttribute(ATTRIB_CATEGORY))
        {
          prog->GetCategory().SetFullName(programElement.attribute(ATTRIB_CATEGORY));
        }
      if (programElement.hasAttribute(ATTRIB_ITEM_NAME))
        {
          prog->SetExportName(programElement.attribute(ATTRIB_ITEM_NAME));
        }

			for (QDomElement shdProgElement = programElement.firstChildElement(ELEMENT_SHADERPROGRAM);
           !shdProgElement.isNull();
					 shdProgElement = shdProgElement.nextSiblingElement(ELEMENT_SHADERPROGRAM))
        {

          Program::ShaderProgram *shdProg = prog->CreateProgram();
          if (shdProgElement.hasAttribute(ATTRIB_NAME))
            {
              shdProg->SetName(shdProgElement.attribute(ATTRIB_NAME));
            }


          for (QDomElement shaderElement = shdProgElement.firstChildElement(ELEMENT_SHADER);
               !shaderElement.isNull();
               shaderElement = shaderElement.nextSiblingElement(ELEMENT_SHADER))
            {
              if (!programElement.hasAttribute(ATTRIB_CATEGORY) || !programElement.hasAttribute(ATTRIB_ITEM_NAME))
                {
                  return false;
                }
              QString name = shaderElement.attribute(ATTRIB_CATEGORY) + "." +
                             shaderElement.attribute(ATTRIB_ITEM_NAME);

              Shader* shader = GetShader(name);
              if (shader)
                {
                  shdProg->AddShader(shader);
                }
            }
          if (!shdProg->GetProgram()->Link())
            {
              printf ("Link: %s\n",
                      shdProg->GetProgram()->GetLinkInfoLog().c_str());
              fflush(stdout);
            }
          prog->AddProgram(shdProg);
        }

      QDomElement bindingsElement = programElement.firstChildElement(ELEMENT_BINDINGS);
      if (!bindingsElement.isNull())
        {
          prog->UpdateBindingValues();
          for (QDomElement bindingElement = bindingsElement.firstChildElement(ELEMENT_BINDING);
               !bindingElement.isNull();
               bindingElement = bindingElement.nextSiblingElement(ELEMENT_BINDING))
            {
              if (bindingElement.hasAttribute(ATTRIB_NAME) && bindingElement.hasAttribute(ATTRIB_BINDING))
                {
                  prog->SetBindingValue(bindingElement.attribute(ATTRIB_NAME), bindingElement.attribute(ATTRIB_BINDING));
                }
            }
          prog->UpdateProgramBindings();
        }


      AddProgram(prog);
    }
  return true;
}

bool MaterialManager::LoadEffects()
{
  QFile file (Session::Get()->GetProjectFolder() + "/effects.xfxs");
  if (!file.open(QIODevice::ReadOnly))
    {
      return false;
    }

  QDomDocument doc;
  if (!doc.setContent(&file))
    {
      file.close();
      return false;
    }
  file.close();

  QDomElement effectsElement = doc.firstChildElement(ELEMENT_EFFECTS);
  for (QDomElement effectElement = effectsElement.firstChildElement(ELEMENT_EFFECT);
       !effectElement.isNull();
       effectElement = effectElement.nextSiblingElement(ELEMENT_EFFECT))
    {
      Effect* effect = CreateEffect();

      if (effectElement.hasAttribute(ATTRIB_CATEGORY))
        {
          effect->GetCategory().SetFullName(effectElement.attribute(ATTRIB_CATEGORY));
        }
      if (effectElement.hasAttribute(ATTRIB_ITEM_NAME))
        {
          effect->SetExportName(effectElement.attribute(ATTRIB_ITEM_NAME));
        }

			QDomElement solverElement = effectElement.firstChildElement (ELEMENT_SOLVER);
			if (!solverElement.isNull ())
				{
					effect->SetSolver (effectElement.text ());
				}

      effect->ClearStages();
			int stage = 0;
			for (QDomElement effectProgElement = effectElement.firstChildElement (ELEMENT_EFFECTPROGRAM);
					 !effectProgElement.isNull ();
					 effectProgElement = effectProgElement.nextSiblingElement (ELEMENT_EFFECTPROGRAM))
				{
					effect->AddStage ();
					for (QDomElement progElement = effectProgElement.firstChildElement(ELEMENT_PROGRAM);
								!progElement.isNull();
								progElement = progElement.nextSiblingElement(ELEMENT_PROGRAM))
						{
							if (progElement.hasAttribute(ATTRIB_PASS) && progElement.hasAttribute(ATTRIB_NAME) && progElement.hasAttribute(ATTRIB_CATEGORY))
								{
									QString pass = progElement.attribute(ATTRIB_PASS);
									QString cat  = progElement.attribute(ATTRIB_CATEGORY);
									QString name = progElement.attribute(ATTRIB_ITEM_NAME);

									QString fullname = cat + "." + name;
									Program* program = GetProgram(fullname);
									effect->SetProgram(stage, pass, program);
								}
						}
					++stage;
				}
      AddEffect(effect);

    }


  return true;
}



bool MaterialManager::LoadMaterials()
{
  QFile file (Session::Get()->GetProjectFolder() + "/materials.xmats");
  if (!file.open(QIODevice::ReadOnly))
    {
      return false;
    }

  QDomDocument doc;
  if (!doc.setContent(&file))
    {
      file.close();
      return false;
    }
  file.close();

  QDomElement materialsElement = doc.firstChildElement(ELEMENT_MATERIALS);

  for (QDomElement materialElement = materialsElement.firstChildElement(ELEMENT_MATERIAL);
       !materialElement.isNull();
       materialElement = materialElement.nextSiblingElement(ELEMENT_MATERIAL))
    {
      Material* material = CreateMaterial();

      if (materialElement.hasAttribute(ATTRIB_CATEGORY))
        {
          material->GetCategory().SetFullName(materialElement.attribute(ATTRIB_CATEGORY));
        }
      if (materialElement.hasAttribute(ATTRIB_ITEM_NAME))
        {
          material->SetExportName(materialElement.attribute(ATTRIB_ITEM_NAME));
        }

      bool defaultMaterial = false;
      QDomElement defaultElement = materialElement.firstChildElement(ELEMENT_DEFAULT);
      if (!defaultElement.isNull())
        {
          defaultMaterial = (defaultElement.text().compare("true", Qt::CaseInsensitive) == 0);
        }

      ceMaterial* mat = material->GetMaterial();
      ceMaterialSpec& spec = mat->GetMaterialSpec();
      ceTexturePack* pack = mat->GetTextures();

      QDomElement specElement = materialElement.firstChildElement(ELEMENT_SPEC);
      if (!specElement.isNull())
        {
          read_color(specElement.firstChildElement(ELEMENT_AMBIENT), spec.Ambient);
          read_color(specElement.firstChildElement(ELEMENT_DIFFUSE), spec.Diffuse);
          read_color(specElement.firstChildElement(ELEMENT_SPECULAR), spec.Specular);
          QDomElement shininessElement = specElement.firstChildElement(ELEMENT_SHININESS);
          if (!shininessElement.isNull())
            {
              spec.Shininess = shininessElement.text().toFloat();
            }
          read_color(specElement.firstChildElement(ELEMENT_EMISSION), spec.Emission);

          QDomElement writeDepthElement = specElement.firstChildElement(ELEMENT_WRITEDEPTH);
          if (!writeDepthElement.isNull())
            {
              spec.WriteDepth = writeDepthElement.text().compare("true", Qt::CaseInsensitive) == 0;
            }
          QDomElement testDepthElement = specElement.firstChildElement(ELEMENT_TESTDEPTH);
          if (!testDepthElement.isNull())
            {
              spec.TestDepth = testDepthElement.text().compare("true", Qt::CaseInsensitive) == 0;
            }
          QDomElement transElement = specElement.firstChildElement(ELEMENT_TRANS);
          if (!transElement.isNull())
            {
              spec.Transparency = transElement.text().compare("true", Qt::CaseInsensitive) == 0;
            }
        }

      QDomElement effectElement = materialElement.firstChildElement(ELEMENT_EFFECT);
      if (!effectElement.isNull())
        {
          if (effectElement.hasAttribute(ATTRIB_CATEGORY) && effectElement.hasAttribute(ATTRIB_NAME))
            {
              const QString& category = effectElement.attribute(ATTRIB_CATEGORY);
              const QString& name = effectElement.attribute(ATTRIB_ITEM_NAME);
              Effect* effect = GetEffect(category+"."+name);
              material->SetEffect(effect);
            }
        }

			QDomElement texturePackElement = materialElement.firstChildElement(ELEMENT_TEXTUREPACK);
			for (QDomElement texturesElement = texturePackElement.firstChildElement (ELEMENT_TEXTURES);
				   !texturesElement.isNull ();
					 texturesElement = texturesElement.nextSiblingElement (ELEMENT_TEXTURES))
				{
					const QString& pass = texturesElement.attribute(ATTRIB_PASS);
					ceRenderPass rp;
					if (pass.compare(PASS_DIFFUSE, Qt::CaseInsensitive) == 0)
						{
							rp = RP_Diffuse;
						}
					else if (pass.compare(PASS_DEPTH, Qt::CaseInsensitive) == 0)
						{
							rp = RP_Depth;
						}
					else if (pass.compare(PASS_SHADOW, Qt::CaseInsensitive) == 0)
						{
							rp = RP_Shadow;
						}
					else if (pass.compare(PASS_STENCIL, Qt::CaseInsensitive) == 0)
						{
							rp = RP_StencilVolume;
						}

					for (QDomElement textureElement = texturesElement.firstChildElement(ELEMENT_TEXTURE);
							 !textureElement.isNull();
							 textureElement = textureElement.nextSiblingElement(ELEMENT_TEXTURE))
						{
							if (textureElement.hasAttribute(ATTRIB_ITEM_NAME) &&
									textureElement.hasAttribute(ATTRIB_CATEGORY) &&
									textureElement.hasAttribute(ATTRIB_STAGE))
								{
									const QString& name = textureElement.attribute(ATTRIB_ITEM_NAME);
									const QString& category = textureElement.attribute(ATTRIB_CATEGORY);
									const QString& stage = textureElement.attribute(ATTRIB_STAGE);


									Texture* texture = GetTexture(category+"."+name);
									if (texture)
										{
											pack->SetTexture(rp, (ceTextureStage)stage.toInt(), texture->GetTexture());
										}
								}
						}
        }
      AddMaterial(material, defaultMaterial);




    }
  return true;
}

/* ******************************************************
 * ******************************************************
 *  Saving
 * ******************************************************
 * ******************************************************/


void MaterialManager::Save(int dv)
{
  if(FLAG(dv, DV_Textures))
    {
      SaveTextures();
    }
  if (FLAG(dv, DV_Shaders))
    {
      SaveShaders();
    }
  if (FLAG(dv, DV_Programs))
    {
      SavePrograms();
    }
  if (FLAG(dv, DV_Effects))
    {
      SaveEffects();
    }
  if (FLAG(dv, DV_Materials))
    {
      SaveMaterials();
    }
}

void MaterialManager::SaveTextures()
{
  QFile file (Session::Get()->GetProjectFolder() + "/textures.xtxts");
  if (!file.open(QIODevice::WriteOnly))
    {
      return;
    }

  QDomDocument doc;

  QDomElement texturesElement = doc.createElement(ELEMENT_TEXTURES);

  for (unsigned i=0; i<GetNumberOfTextures(); i++)
    {
      Texture* texture = GetTexture(i);
      if (!texture) continue;

      QDomElement textureElement = doc.createElement(ELEMENT_TEXTURE);

      textureElement.setAttribute(ATTRIB_CATEGORY, texture->GetCategory().GetFullName());
			textureElement.setAttribute(ATTRIB_ITEM_NAME, texture->GetExportName());
      textureElement.setAttribute(ATTRIB_NAME, texture->GetCategory().GetFullName() + "." + texture->GetExportName());
			
			textureElement.setAttribute(ATTRIB_TYPE, "texture2d");


      // write the texture filename
      QString filename = texture->GetFilename();

      QDomElement filenameElement = doc.createElement(ELEMENT_FILENAME);
      filenameElement.appendChild(doc.createTextNode(filename));
      textureElement.appendChild(filenameElement);

      // write the Wrap
      QString wrap = WRAP_REPEAT;
			bool writeWrap = true;
      switch (texture->GetWrap())
        {
			  case Texture::Wrap_Default:				writeWrap = false;					break;
        case Texture::Wrap_Repeat:        wrap = WRAP_REPEAT;         break;
        case Texture::Wrap_Clamp:         wrap = WRAP_CLAMP;          break;
        case Texture::Wrap_ClampToEdge:   wrap = WRAP_CLAMPTOEDGE;    break;
        case Texture::Wrap_ClampToBorder: wrap = WRAP_CLAMPTOBORDER;  break;
        default: break;
        }
			if (writeWrap)
				{
					QDomElement wrapElement = doc.createElement("wrap");
					wrapElement.appendChild(doc.createTextNode(wrap));
					textureElement.appendChild(wrapElement);
				}

      QString filter = FILTER_ANISOTROPIC;
			bool writeFilter = true;
      switch (texture->GetFilter())
        {
			  case Texture::Filter_Default:     writeFilter = false;					break;
        case Texture::Filter_Anisotropic: filter = FILTER_ANISOTROPIC;  break;
        case Texture::Filter_Trilinear:   filter = FILTER_TRILINEAR;    break;
        case Texture::Filter_Bilinear:    filter = FILTER_BILINEAR;     break;
        case Texture::Filter_Nearest:     filter = FILTER_NEAREST;      break;
        default: break;
        }
			if (writeFilter)
				{
					QDomElement filterElement = doc.createElement(ELEMENT_FILTER);
					filterElement.appendChild(doc.createTextNode(filter));
					textureElement.appendChild(filterElement);
				}

			int anisotropy = texture->GetAnisotropy ();
			if (anisotropy > 0)
				{
					QString anisotropy = QString ("%1").arg(texture->GetAnisotropy());
					QDomElement anisotropyElement = doc.createElement(ELEMENT_ANISOTROPY);
					anisotropyElement.appendChild(doc.createTextNode(anisotropy));
					textureElement.appendChild(anisotropyElement);
				}

			QString mipMap = MIPMAP_ON;
			bool writeMipMap = true;
			switch (texture->GetMipMap ())
				{
				case Texture::MipMap_Default:	writeMipMap = false;	break;
				case Texture::MipMap_On: mipMap = MIPMAP_ON;				break;
				case Texture::MipMap_Off: mipMap = MIPMAP_OFF;			break;
				default: break;
				}
			if (writeMipMap)
				{
					QDomElement mipMapElement = doc.createElement (ELEMENT_MIPMAP);
					mipMapElement.appendChild(doc.createTextNode (mipMap));
					textureElement.appendChild (mipMapElement);
				}
      texturesElement.appendChild(textureElement);
    }

  doc.appendChild(texturesElement);


  file.write(doc.toByteArray(4));
  file.close();
}

void MaterialManager::SaveShaders()
{
  QFile file (Session::Get()->GetProjectFolder() + "/shaders.xshds");
  if (!file.open(QIODevice::WriteOnly))
    {
      return;
    }

  QDomDocument doc;

  QDomElement shadersElement = doc.createElement(ELEMENT_SHADERS);

  for (unsigned i=0; i<GetNumberOfShaders(); i++)
    {
      Shader* shader = GetShader(i);
      if (!shader)
        {
          continue;
        }

      QDomElement shaderElement = doc.createElement(ELEMENT_SHADER);


      shaderElement.setAttribute(ATTRIB_CATEGORY, shader->GetCategory().GetFullName());
      shaderElement.setAttribute(ATTRIB_ITEM_NAME, shader->GetExportName());
			shaderElement.setAttribute(ATTRIB_NAME, shader->GetCategory().GetFullName() + "." + shader->GetExportName());


      QDomElement typeElement = doc.createElement(ELEMENT_TYPE);
      switch (shader->GetShaderType())
        {
        case ST_Vertex:
          typeElement.appendChild(doc.createTextNode(SHADER_VERTEX));
          break;
        case ST_Fragment:
          typeElement.appendChild(doc.createTextNode(SHADER_FRAGMENT));
          break;
        }
      shaderElement.appendChild(typeElement);

      QDomElement codeElement = doc.createElement(ELEMENT_CODE);
      codeElement.appendChild(doc.createCDATASection(shader->GetCode()));
      shaderElement.appendChild(codeElement);

      shadersElement.appendChild(shaderElement);

    }

  doc.appendChild(shadersElement);

  file.write(doc.toByteArray(4));
  file.close();
}

void MaterialManager::SavePrograms()
{
  QFile file (Session::Get()->GetProjectFolder() + "/programs.xprgs");
  if (!file.open(QIODevice::WriteOnly))
    {
      return;
    }

  QDomDocument doc;
  QDomElement programsElement = doc.createElement(ELEMENT_PROGRAMS);
  for (unsigned i=0; i<GetNumberOfPrograms(); i++)
    {
      Program* prog = GetProgram(i);
      if (!prog)
        {
          continue;
        }

      QDomElement programElement = doc.createElement(ELEMENT_PROGRAM);
      programElement.setAttribute(ATTRIB_CATEGORY, prog->GetCategory().GetFullName());
      programElement.setAttribute(ATTRIB_ITEM_NAME, prog->GetExportName());
      programElement.setAttribute(ATTRIB_NAME, prog->GetCategory().GetFullName() + "." + prog->GetExportName());


      for (unsigned j=0; j<prog->GetNumberOfPrograms(); j++)
        {
          Program::ShaderProgram *shdProg = prog->GetProgram(j);
          if (!shdProg)
            {
              continue;
            }

					QDomElement shdProgElement = doc.createElement(ELEMENT_SHADERPROGRAM);
          shdProgElement.setAttribute(ATTRIB_NAME, shdProg->GetName());


          // set the requirements
					QDomElement requirementsElement = doc.createElement(ELEMENT_REQUIREMENTS);
          QDomElement requirementElement = doc.createElement(ELEMENT_REQUIREMENT);
          QDomElement versionElement = doc.createElement(ELEMENT_VERSION);
          QString version = Shader::GetShaderVersionString(shdProg->GetVersion());
          versionElement.appendChild(doc.createTextNode(version));
          requirementElement.appendChild(versionElement);
					requirementsElement.appendChild(requirementElement);

          shdProgElement.appendChild(requirementsElement);

          for (unsigned k=0; k<shdProg->GetNumberOfShaders(); k++)
            {
              Shader* shader = shdProg->GetShader(k);
              if (!shader)
                {
                  continue;
                }



              QDomElement shaderElement = doc.createElement(ELEMENT_SHADER);
              shaderElement.setAttribute(ATTRIB_CATEGORY, shader->GetCategory().GetFullName());
              shaderElement.setAttribute(ATTRIB_ITEM_NAME, shader->GetExportName());
							shaderElement.setAttribute(ATTRIB_NAME, shader->GetCategory().GetFullName() + "." + shader->GetExportName());
              shdProgElement.appendChild(shaderElement);
            }

          programElement.appendChild(shdProgElement);
        }


      QDomElement bindingsElement = doc.createElement(ELEMENT_BINDINGS);
      for (unsigned j=0; j<prog->GetNumberOfBindings(); j++)
        {
          QString key   = prog->GetBindingKey(j);
          QString value = prog->GetBindingValue(j);

          QDomElement bindingElement = doc.createElement(ELEMENT_BINDING);
          bindingElement.setAttribute(ATTRIB_NAME, key);
          bindingElement.setAttribute(ATTRIB_BINDING, value);
          bindingsElement.appendChild(bindingElement);
        }
      programElement.appendChild(bindingsElement);

      programsElement.appendChild(programElement);
    }

  doc.appendChild(programsElement);

  file.write(doc.toByteArray(4));
  file.close();

}

void MaterialManager::SaveEffects()
{
  QFile file (Session::Get()->GetProjectFolder() + "/effects.xfxs");
  if (!file.open(QIODevice::WriteOnly))
    {
      return;
    }

  QDomDocument doc;
  QDomElement effectsElement = doc.createElement(ELEMENT_EFFECTS);

  for (unsigned i=0; i<GetNumberOfEffects(); i++)
    {
      Effect* effect = GetEffect(i);
      if (!effect)
        {
          continue;
        }


      QDomElement effectElement = doc.createElement(ELEMENT_EFFECT);
      effectElement.setAttribute(ATTRIB_CATEGORY, effect->GetCategory().GetFullName());
      effectElement.setAttribute(ATTRIB_ITEM_NAME, effect->GetExportName());
			effectElement.setAttribute(ATTRIB_NAME, effect->GetCategory().GetFullName() + "." + effect->GetExportName());

			QDomElement solverElement = doc.createElement (ELEMENT_SOLVER);
			solverElement.appendChild (doc.createTextNode (effect->GetSolver ()));
			effectElement.appendChild (solverElement);

			for (unsigned j=0; j<effect->GetNumberOfStages (); j++)
				{
					QDomElement effectProgElement = doc.createElement (ELEMENT_EFFECTPROGRAM);

					for (unsigned k=0; k<effect->GetNumberOfPrograms(j); k++)
						{
							QString name = effect->GetProgramName(j, k);
							Program* prog = effect->GetProgram(j, name);
							if (!prog)
								{
									continue;
								}

							QDomElement progElement = doc.createElement(ELEMENT_PROGRAM);
							progElement.setAttribute(ATTRIB_PASS, name);
							progElement.setAttribute(ATTRIB_ITEM_NAME, prog->GetExportName());
							progElement.setAttribute(ATTRIB_CATEGORY, prog->GetCategory().GetFullName());
							progElement.setAttribute(ATTRIB_NAME, prog->GetCategory().GetFullName() + "." + prog->GetExportName());
							effectProgElement.appendChild(progElement);
						}
					effectElement.appendChild(effectProgElement);
				}

      effectsElement.appendChild(effectElement);
    }


  doc.appendChild(effectsElement);

  file.write(doc.toByteArray(4));
  file.close();
}

void MaterialManager::SaveMaterials()
{
  QFile file (Session::Get()->GetProjectFolder() + "/materials.xmats");
  if (!file.open(QIODevice::WriteOnly))
    {
      return;
    }

  QDomDocument doc;
  QDomElement materialsElement = doc.createElement(ELEMENT_MATERIALS);

  Material* defaultMat = GetDefaultMaterial();

  for (unsigned i=0; i<GetNumberOfMaterials(); i++)
    {
      Material* material = GetMaterial(i);
      const ceMaterial* mat = material->GetMaterial();
      if (!mat)
        {
          continue;
        }

      const ceTexturePack* pack = mat->GetTextures();
      const ceMaterialSpec& spec = mat->GetMaterialSpec();

      QDomElement materialElement = doc.createElement(ELEMENT_MATERIAL);
      materialElement.setAttribute(ATTRIB_CATEGORY, material->GetCategory().GetFullName());
      materialElement.setAttribute(ATTRIB_ITEM_NAME, material->GetExportName());
      materialElement.setAttribute(ATTRIB_NAME, material->GetCategory().GetFullName() + "." + material->GetExportName());

      QDomElement defaultElement = doc.createElement(ELEMENT_DEFAULT);
      if (material == defaultMat)
        {
          defaultElement.appendChild(doc.createTextNode("true"));
        }
      else
        {
          defaultElement.appendChild(doc.createTextNode("false"));
        }
      materialElement.appendChild(defaultElement);

      QDomElement specElement = doc.createElement(ELEMENT_SPEC);

      QDomElement ambientElement = doc.createElement(ELEMENT_AMBIENT);
      write_color (ambientElement, spec.Ambient);
      specElement.appendChild(ambientElement);

      QDomElement diffuseElement = doc.createElement(ELEMENT_DIFFUSE);
      write_color (diffuseElement, spec.Diffuse);
      specElement.appendChild(diffuseElement);

      QDomElement specularElement = doc.createElement(ELEMENT_SPECULAR);
      write_color (specularElement, spec.Specular);
      specElement.appendChild(specularElement);

      QDomElement shininessElement = doc.createElement(ELEMENT_SHININESS);
      shininessElement.appendChild(doc.createTextNode(QString("%1").arg(spec.Shininess)));
      specElement.appendChild(shininessElement);

      QDomElement emissionElement = doc.createElement(ELEMENT_EMISSION);
      write_color (emissionElement, spec.Emission);
      specElement.appendChild(emissionElement);

      QDomElement writeDepthElement = doc.createElement(ELEMENT_WRITEDEPTH);
      writeDepthElement.appendChild(doc.createTextNode(spec.WriteDepth ? "true" : "false"));
      specElement.appendChild(writeDepthElement);

      QDomElement testDepthElement = doc.createElement(ELEMENT_TESTDEPTH);
      testDepthElement.appendChild(doc.createTextNode(spec.TestDepth ? "true" : "false"));
      specElement.appendChild(testDepthElement);

      QDomElement transElement = doc.createElement(ELEMENT_TRANS);
      transElement.appendChild(doc.createTextNode(spec.Transparency ? "true" : "false"));
      specElement.appendChild(transElement);

      materialElement.appendChild(specElement);


      const Effect* effect = material->GetEffect();
      if (effect)
        {
          QDomElement effectElement = doc.createElement(ELEMENT_EFFECT);
          effectElement.setAttribute(ATTRIB_CATEGORY, effect->GetCategory().GetFullName());
          effectElement.setAttribute(ATTRIB_ITEM_NAME, effect->GetExportName());
          effectElement.setAttribute(ATTRIB_NAME, effect->GetCategory().GetFullName() + "." +effect->GetExportName());
          materialElement.appendChild(effectElement);
        }

			QDomElement texturePackElement = doc.createElement(ELEMENT_TEXTUREPACK);


      for (unsigned rp=0; rp<RP_Count; rp++)
        {
          QString rpName;
          switch (rp)
            {
            case RP_Diffuse:
              rpName = PASS_DIFFUSE;
              break;
            case RP_Depth:
              rpName = PASS_DEPTH;
              break;
            case RP_Shadow:
              rpName = PASS_SHADOW;
              break;
            case RP_StencilVolume:
              rpName = PASS_STENCIL;
              break;
            default:
              continue;
            }
		      QDomElement texturesElement = doc.createElement(ELEMENT_TEXTURES);
					texturesElement.setAttribute (ATTRIB_PASS, rpName);

          for (unsigned ts=0; ts<TS_Count; ts++)
            {
              Texture* texture = FindTexture(pack->GetTexture((ceRenderPass)rp, (ceTextureStage)ts));
              if (!texture)
                {
                  continue;
                }

              QDomElement textureElement = doc.createElement(ELEMENT_TEXTURE);
              textureElement.setAttribute(ATTRIB_STAGE, ts);
              textureElement.setAttribute(ATTRIB_CATEGORY, texture->GetCategory().GetFullName());
              textureElement.setAttribute(ATTRIB_ITEM_NAME, texture->GetExportName());
              textureElement.setAttribute(ATTRIB_NAME, texture->GetCategory().GetFullName() + "." + texture->GetExportName());
              texturesElement.appendChild(textureElement);
            }

					texturePackElement.appendChild (texturesElement);
        }
      materialElement.appendChild(texturePackElement);


      materialsElement.appendChild(materialElement);
    }

  doc.appendChild(materialsElement);

  file.write(doc.toByteArray(4));
  file.close ();
}
